package cn.tedu.erp.project.ex.handler;

import cn.tedu.erp.project.ex.ServiceException;
import cn.tedu.erp.project.web.JsonResult;
import cn.tedu.erp.project.web.ServiceCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;

/**
 * 全局异常处理器
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    public GlobalExceptionHandler() {
        log.debug("创建全局异常处理器类对象：GlobalExceptionHandler");
    }

    /**
     * 处理业务中的异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler
    public JsonResult<Void> handlerServiceException(ServiceException e) {
        return JsonResult.fail(e);
    }

    /**
     * 处理validation框架检查失败时请求参数格式异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler
    public JsonResult<Void> handleBindException(BindException e) {
        String message = e.getFieldError().getDefaultMessage();
        return JsonResult.fail(ServiceCode.ERROR_BAD_REQUEST, message);
    }

    /**
     * 处理检查参数的异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler
    public JsonResult<Void> handleConstraintViolationException(ConstraintViolationException e) {
        StringBuilder stringBuilder = new StringBuilder();
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        for (ConstraintViolation<?> constraintViolation : constraintViolations) {
            stringBuilder.append(constraintViolation.getMessage());
        }
        String message = stringBuilder.toString();
        return JsonResult.fail(ServiceCode.ERROR_BAD_REQUEST, message);
    }

    /**
     * 处理用户登录失败
     *
     * @param e
     * @return
     */
    @ExceptionHandler(
            {BadCredentialsException.class,
                    InternalAuthenticationServiceException.class
            }
    )
    public JsonResult<Void> handleAuthenticationException(AuthenticationException e) {
        log.warn("程序运行过程中出现AuthenticationException异常,统一处理");
        log.warn("异常名称:{}", e.getClass().getName());
        log.warn("异常信息:{}", e.getMessage());
        String message = "登录失败,用户名或密码错误!";
        return JsonResult.fail(ServiceCode.ERROR_UNAUTHORIZED, message);
    }

    /**
     * 处理用户被禁用
     *
     * @param e
     * @return
     */
    @ExceptionHandler
    public JsonResult<Void> handleDisabledException(DisabledException e) {
        String message = "登录失败,该用户被禁用!";
        log.warn("异常名称:{}", e.getClass().getName());
        log.warn("异常信息:{}", e.getMessage());
        return JsonResult.fail(ServiceCode.ERROR_UNAUTHORIZED_DISABLE, message);
    }

    /**
     * 处理用户权限
     * @param e
     * @return
     */
    @ExceptionHandler
    public JsonResult<Void> handleAccessDeniedExceptionE(AccessDeniedException e) {
        String message = "拒绝访问,该用户没有权限!";
        log.warn("异常名称:{}", e.getClass().getName());
        log.warn("异常信息:{}", e.getMessage());
        return JsonResult.fail(ServiceCode.ERROR_FORBIDDEN, message);
    }

    /**
     * 处理以上异常外的其他异常
     *
     * @param e
     * @return
     */
    // 注意：以下方法存在的意义主要在于：避免因为某个异常未被处理，导致服务器端响应500错误.
    //后续优化
    @ExceptionHandler
    public JsonResult<Void> handleThrowable(Throwable e) {
        String message = "服务器繁忙...(开发过程中看到此提示,请检查控制台异常抛出,并补充处理)";
        //log.warn("异常类型:{}",e.getClass().getName());
        //log.warn("异常信息:{}",e.getMessage());  项目上线时用
        // 找到异常的跟踪信息
        e.printStackTrace();//注意:在生产环境中,禁止使用此语句!
        return JsonResult.fail(ServiceCode.ERROR_UNKNOWN, message);
    }
}
